/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.formserver.vmmanager.service;

import com.inspur.edp.formserver.viewmodel.exception.ViewModelException;
import com.inspur.edp.formserver.vmapi.ViewModelService;
import com.inspur.edp.formserver.vmapi.dto.ViewModelAuthInfoDto;
import com.inspur.edp.formserver.vmapi.dto.ViewModelAuthSecurityDto;
import com.inspur.edp.formserver.vmapi.dto.ViewModelExtendDto;
import com.inspur.edp.formserver.vmmanager.exception.VoManagerErrorCodes;
import io.iec.edp.caf.commons.transaction.JpaTransaction;
import io.iec.edp.caf.commons.utils.CollectionUtils;
import io.iec.edp.caf.securityentry.api.common.AuthType;
import io.iec.edp.caf.securityentry.api.data.SecurityEntry;
import io.iec.edp.caf.securityentry.api.data.SecurityEntry4RuleDTO;
import io.iec.edp.caf.securityentry.api.manager.SecurityEntryService;
import org.apache.commons.lang3.StringUtils;

import java.util.Map;
import java.util.Set;

/**
 * 视图对象服务实现类
 */
public class ViewModelServiceImpl implements ViewModelService {
    // 功能权限extType
    private static final String FUNC_SECURITY_EXTTYPE = "VM";
    // 权限规则extType
    private static final String PERMRULE_AUTH_SECURITY_EXTTYPE = "VMRule";
    // 数据权限extType
    private static final String AUTH_SECURITY_EXTTYPE = "VM";
    // 列权限extType
    private static final String COLUMN_SECURITY_EXTTYPE = "VM";
    // 权限服务
    private final SecurityEntryService securityEntryService;

    public ViewModelServiceImpl(SecurityEntryService securityEntryService) {
        this.securityEntryService = securityEntryService;
    }

    /**
     * 获取视图对象扩展信息(非元数据内容, 目前主要为权限信息)
     *
     * @param viewModelId 视图对象id
     * @return 视图对象扩展信息
     */
    @Override
    public ViewModelExtendDto getViewModelExtendInfo(String viewModelId) {
        // 组织返回数据结构
        ViewModelExtendDto viewModelExtendDto = new ViewModelExtendDto();
        // 加载权限信息
        viewModelExtendDto.setAuthInfo(this.loadViewModelAuthInfo(viewModelId));
        return viewModelExtendDto;
    }

    /**
     * 保存视图对象的扩展信息
     *
     * @param viewModelId         待保存的视图对象ID
     * @param viewModelExtendInfo 待保存的扩展信息
     * @param authIdsToDelete     需要删除的权限ID
     */
    @Override
    public void saveViewModelExtendInfo(String viewModelId, ViewModelExtendDto viewModelExtendInfo,
                                        Set<String> authIdsToDelete) {
        // 获取权限信息
        ViewModelAuthInfoDto authInfo = viewModelExtendInfo.getAuthInfo();
        JpaTransaction tran = JpaTransaction.getTransaction();
        try {
            // 事务开始
            tran.begin();
            // 保存功能权限
            handleFuncSecurity(viewModelId, authIdsToDelete, authInfo.getFuncSecurity());

            // 保存数据权限信息
            Map<String, ViewModelAuthSecurityDto> authSecurityMap = authInfo.getAuthSecurityMap();
            if (!CollectionUtils.isEmpty(authSecurityMap)) {
                for (ViewModelAuthSecurityDto value : authSecurityMap.values()) {
                    if (value == null) {
                        continue;
                    }
                    // 判断是否为权限规则
                    if (value.isPermRule()) {
                        // 保存权限规则
                        handlePermSecurity(viewModelId, authIdsToDelete, value.getPermSecurityEntry());
                        // 若存在数据权限则移除
                        if (value.getAuthSecurityEntry() != null && StringUtils.isNotBlank(value.getAuthSecurityEntry().getId())) {
                            authIdsToDelete.add(value.getAuthSecurityEntry().getId());
                        }

                    } else {
                        // 保存数据权限
                        handleAuthSecurity(viewModelId, authIdsToDelete, value.getAuthSecurityEntry());
                        // 若存在数据权限则移除
                        if (value.getPermSecurityEntry() != null && StringUtils.isNotBlank(value.getPermSecurityEntry().getId())) {
                            authIdsToDelete.add(value.getPermSecurityEntry().getId());
                        }
                    }
                }
            }

            // 保存列权限
            handleColumnSecurity(viewModelId, authIdsToDelete, authInfo.getColumnSecurity());

            // 删除权限
            if (!CollectionUtils.isEmpty(authIdsToDelete)) {
                for (String authId : authIdsToDelete) {
                    securityEntryService.deleteById(authId);
                }
            }

            // 保存帮助信息
            if (viewModelExtendInfo.getHelpInfo() != null
                    && !CollectionUtils.isEmpty(viewModelExtendInfo.getHelpInfo().getHelpSecurityEntries())) {
                viewModelExtendInfo.getHelpInfo().getHelpSecurityEntries().forEach(securityEntryService::saveAuthSecurityEntry);
            }
            tran.commit();
        } catch (Throwable t) {
            tran.rollback();
            throw new ViewModelException(VoManagerErrorCodes.GSP_VIEWOBJECT_MANAGER_0111, t, viewModelId);
        }
    }

    private void handleColumnSecurity(String viewModelId, Set<String> authIdsToDelete, SecurityEntry columnSecurity) {
        if (isConfigedColumnSecurityEntry(columnSecurity)) {
            // 有效权限处理逻辑
            if (StringUtils.isBlank(columnSecurity.getId())) {
                // ID不存在时直接保存
                columnSecurity.setEx1(viewModelId);
                columnSecurity.setExtType(COLUMN_SECURITY_EXTTYPE);
                columnSecurity.setAuthType(AuthType.ColumnLevel);
                securityEntryService.saveVOColumn(columnSecurity);
            } else if (!authIdsToDelete.contains(columnSecurity.getId())) {
                // ID存在且不在删除列表时保存
                columnSecurity.setEx1(viewModelId);
                columnSecurity.setExtType(COLUMN_SECURITY_EXTTYPE);
                columnSecurity.setAuthType(AuthType.ColumnLevel);
                securityEntryService.saveVOColumn(columnSecurity);
            }
        } else {
            // 无效权限处理逻辑
            if (StringUtils.isNotBlank(columnSecurity.getId())) {
                // ID存在时加入删除列表
                authIdsToDelete.add(columnSecurity.getId());
            }
            // ID不存在时无需处理
        }
    }

    private void handleAuthSecurity(String viewModelId, Set<String> authIdsToDelete, SecurityEntry authSecurityEntry) {
        if (isConfigedAuthSecurityEntry(authSecurityEntry)) {
            // 有效权限处理逻辑
            if (StringUtils.isBlank(authSecurityEntry.getId())) {
                // ID不存在时直接保存
                authSecurityEntry.setEx1(viewModelId);
                authSecurityEntry.setExtType(AUTH_SECURITY_EXTTYPE);
                securityEntryService.saveAuthSecurityEntry(authSecurityEntry);
            } else if (!authIdsToDelete.contains(authSecurityEntry.getId())) {
                // ID存在且不在删除列表时保存
                authSecurityEntry.setEx1(viewModelId);
                authSecurityEntry.setExtType(AUTH_SECURITY_EXTTYPE);
                securityEntryService.saveAuthSecurityEntry(authSecurityEntry);
            }
        } else {
            // 无效权限处理逻辑
            if (StringUtils.isNotBlank(authSecurityEntry.getId())) {
                // ID存在时加入删除列表
                authIdsToDelete.add(authSecurityEntry.getId());
            }
            // ID不存在时无需处理
        }
    }

    private void handlePermSecurity(String viewModelId, Set<String> authIdsToDelete, SecurityEntry4RuleDTO permSecurityEntry) throws Exception {
        if (isConfigedPermSecurityEntry(permSecurityEntry)) {
            // 有效权限处理逻辑
            if (StringUtils.isBlank(permSecurityEntry.getId())) {
                // ID不存在时直接保存
                permSecurityEntry.setEx1(viewModelId);
                permSecurityEntry.setExtType(PERMRULE_AUTH_SECURITY_EXTTYPE);
                securityEntryService.saveAuthRuleSecurityEntryDTO(permSecurityEntry);
            } else if (!authIdsToDelete.contains(permSecurityEntry.getId())) {
                // ID存在且不在删除列表时保存
                permSecurityEntry.setEx1(viewModelId);
                permSecurityEntry.setExtType(PERMRULE_AUTH_SECURITY_EXTTYPE);
                securityEntryService.saveAuthRuleSecurityEntryDTO(permSecurityEntry);
            }
        } else {
            // 无效权限处理逻辑
            if (StringUtils.isNotBlank(permSecurityEntry.getId())) {
                // ID存在时加入删除列表
                authIdsToDelete.add(permSecurityEntry.getId());
            }
            // ID不存在时无需处理
        }
    }

    private void handleFuncSecurity(String viewModelId, Set<String> authIdsToDelete, SecurityEntry funcSecurity) {
        if (isConfigedFuncSecurityEntry(funcSecurity)) {
            // 有效权限处理逻辑
            if (StringUtils.isBlank(funcSecurity.getId())) {
                // ID不存在时直接保存
                funcSecurity.setEx1(viewModelId);
                funcSecurity.setExtType(FUNC_SECURITY_EXTTYPE);
                securityEntryService.saveFuncSecurityEntry(funcSecurity);
            } else if (!authIdsToDelete.contains(funcSecurity.getId())) {
                // ID存在且不在删除列表时保存
                funcSecurity.setEx1(viewModelId);
                funcSecurity.setExtType(FUNC_SECURITY_EXTTYPE);
                securityEntryService.saveFuncSecurityEntry(funcSecurity);
            }
        } else {
            // 无效权限处理逻辑
            if (funcSecurity != null && StringUtils.isNotBlank(funcSecurity.getId())) {
                // ID存在时加入删除列表
                authIdsToDelete.add(funcSecurity.getId());
            }
            // ID不存在时无需处理
        }
    }

    @Override
    public ViewModelAuthInfoDto loadViewModelAuthInfo(String viewModelId) {
        ViewModelAuthInfoDto authInfo = new ViewModelAuthInfoDto();
        // 组织视图对象的权限信息
        SecurityEntry funcSecuritySelect = new SecurityEntry();

        // 查询视图对象的功能权限
        funcSecuritySelect.setEx1(viewModelId);
        funcSecuritySelect.setExtType(FUNC_SECURITY_EXTTYPE);
        funcSecuritySelect.setAuthType(AuthType.Func);
        SecurityEntry funcSecurityEntry = securityEntryService.getFuncSecurityEntry(funcSecuritySelect);
        authInfo.setFuncSecurity(funcSecurityEntry);

        // 查询视图对象的数据权限
        SecurityEntry4RuleDTO permAuthSecuritySelect = new SecurityEntry4RuleDTO();
        permAuthSecuritySelect.setEx1(viewModelId);
        permAuthSecuritySelect.setExtType(PERMRULE_AUTH_SECURITY_EXTTYPE);
        SecurityEntry4RuleDTO permRuleSecurityEntry = securityEntryService.getAuthRuleSecurityEntryDTO(permAuthSecuritySelect);

        SecurityEntry authSecuritySelect = new SecurityEntry();
        authSecuritySelect.setEx1(viewModelId);
        authSecuritySelect.setExtType(AUTH_SECURITY_EXTTYPE);
        authSecuritySelect.setAuthType(AuthType.Auth);
        SecurityEntry authSecurityEntry = securityEntryService.getAuthSecurityEntry(authSecuritySelect);
        authInfo.addAuthSecurityEntry(viewModelId, authSecurityEntry, permRuleSecurityEntry);

        // 查询视图对象的列权限
        SecurityEntry columnSecuritySelect = new SecurityEntry();
        columnSecuritySelect.setEx1(viewModelId);
        columnSecuritySelect.setExtType(COLUMN_SECURITY_EXTTYPE);
        columnSecuritySelect.setAuthType(AuthType.ColumnLevel);
        authInfo.setColumnSecurity(securityEntryService.getVOColumn(columnSecuritySelect));

        return authInfo;
    }

    /**
     * 判断功能权限是否已配置
     * 校验规则：
     * 1. 必须包含至少一个功能操作关系（opRelations）且关系中操作（operationId）不能为空
     *
     * @param funcSecurityEntry 功能权限
     * @return true-已配置有效权限需要保存，false-无需保存
     */
    private boolean isConfigedFuncSecurityEntry(SecurityEntry funcSecurityEntry) {
        return funcSecurityEntry!=null
                && !CollectionUtils.isEmpty(funcSecurityEntry.getOpRelations())
                && funcSecurityEntry.getOpRelations().stream().anyMatch(item -> StringUtils.isNotBlank(item.getOperationId()));
    }

    /**
     * 校验数据权限配置有效性
     * 需要满足：
     * 1. 权限对象ID不能为空
     * 2. 至少配置一个认证字段（authFields）
     *
     * @param authSecurityEntry 数据权限
     * @return true-需要保存的合法配置
     */
    private boolean isConfigedAuthSecurityEntry(SecurityEntry authSecurityEntry) {
        return authSecurityEntry != null
                && StringUtils.isNotBlank(authSecurityEntry.getAuthObjIds())
                && !CollectionUtils.isEmpty(authSecurityEntry.getAuthFields());
    }
    /**
     * 校验规则型权限配置有效性
     * 需满足：
     * 1. 规则规则结构必须存在（authorizationDTO）
     * 2. 规则规则列表不能为空（authEntryRuleList）
     * 3. 规则列表不能为空（securityRuleEntryList）
     *
     * @param permSecurityEntry 规则权限
     * @return true-合法配置需要保存
     */
    private boolean isConfigedPermSecurityEntry(SecurityEntry4RuleDTO permSecurityEntry) {
        return permSecurityEntry != null
                && permSecurityEntry.getAuthorizationDTO() != null
                && !CollectionUtils.isEmpty(permSecurityEntry.getAuthorizationDTO().getAuthEntryRuleList())
                && !CollectionUtils.isEmpty(permSecurityEntry.getSecurityRuleEntryList());
    }
    /**
     * 校验列权限配置有效性
     * 需要满足：
     * 1. 权限对象ID不为空
     * 2. 必须且只能配置一个字段的权限
     * 3. 该字段必须包含：
     *    - 列集合不能为空（authFieldElements）
     *    - 必须且只能配置一个操作关系
     *
     * @param columnSecurityEntry 列级权限条目
     * @return true-符合要求的列级权限配置
     */
    private boolean isConfigedColumnSecurityEntry(SecurityEntry columnSecurityEntry) {
        return columnSecurityEntry != null
                && StringUtils.isNotBlank(columnSecurityEntry.getAuthObjIds())
                && columnSecurityEntry.getAuthFields() != null
                && columnSecurityEntry.getAuthFields().size() == 1
                && columnSecurityEntry.getAuthFields().get(0) != null
                && !CollectionUtils.isEmpty(columnSecurityEntry.getAuthFields().get(0).getAuthFieldElements())
                && columnSecurityEntry.getAuthFields().get(0).getAuthOpRelations() != null
                && columnSecurityEntry.getAuthFields().get(0).getAuthOpRelations().size() == 1;
    }
}
